package org.xiaoyu.rpc.seralization.support;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.esotericsoftware.kryo.util.Pool;
import org.xiaoyu.rpc.seralization.Serializer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * Created By Have
 * 2021/7/22 16:10
 */
public class Hessian2Serializer implements Serializer {
    private static ThreadLocal<Hessian2Output> OUTPUT_TL = ThreadLocal.withInitial(() -> {
        Hessian2Output h2o = new Hessian2Output(null);
        h2o.setSerializerFactory(new SerializerFactory());
        h2o.setCloseStreamOnClose(true);
        return h2o;
    });

    private static ThreadLocal<Hessian2Input> INPUT_TL = ThreadLocal.withInitial(() -> {
        Hessian2Input h2i = new Hessian2Input(null);
        h2i.setSerializerFactory(new SerializerFactory());
        h2i.setCloseStreamOnClose(true);
        return h2i;
    });

    @Override
    public int getSerializerAlgorithm() {
        return SerializerAlgorithm.HESSIAN2.getType();
    }

    @Override
    public byte[] serializer(Object obj) {
        Hessian2Output h2o = Hessain2ObjPool.POOL.obtain();
        h2o.reset();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            h2o.init(bos);
            h2o.writeObject(obj);
            h2o.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            Hessain2ObjPool.POOL.free(h2o);
        }
    }

    @Override
    public <T> T deserializer(Class<T> clazz, byte[] bytes) {
        Hessian2Input h2i = Hessain2InPool.POOL.obtain();
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            h2i.init(bis);
            T object = (T) h2i.readObject();
            return object;
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            h2i.reset();
            Hessain2InPool.POOL.free(h2i);
        }
    }

    private static class Hessain2ObjPool {
        private static final Pool<Hessian2Output> POOL;

        static {
            POOL = new Pool<Hessian2Output>(true, false, 64) {
                @Override
                protected Hessian2Output create() {
                    Hessian2Output h2o = new Hessian2Output(null);
                    h2o.setSerializerFactory(new SerializerFactory());
                    h2o.setCloseStreamOnClose(true);
                    return h2o;
                }
            };
        }
    }

    private static class Hessain2InPool {
        private static final Pool<Hessian2Input> POOL;

        static {
            POOL = new Pool<Hessian2Input>(true, false, 64) {
                @Override
                protected Hessian2Input create() {
                    Hessian2Input h2i = new Hessian2Input(null);
                    h2i.setSerializerFactory(new SerializerFactory());
                    h2i.setCloseStreamOnClose(true);
                    return h2i;
                }
            };
        }
    }
}
